import CMDate from '../date/cmDate.js'

import Base from '../bagua/base.js'
import Gan from '../bagua/gan.js'
import Zhi from '../bagua/zhi.js'
import Ganzhi from '../bagua/ganzhi.js'
import God12 from '../bagua/god12.js'

import BaseStage from './baseStage.js'

// 大运的步数
const BIG_DESTINY_NUM = 9
// 每步大运的长度
const BIG_DESTINY_OFFSET = 10
// 流年的年数
const BAZI_YEARS_NUM = 100
// 起运，每一日的天数, 这里121.7474参考了了地球公转回归年的数据，365.2422 / 3，有误差在所难免
const DESTINY_DAY_VALUE = 121.7474

class Bazi extends BaseStage {
  constructor (options = {}) {
    super(options)
    
    this.initBazi()
  }
  
  initBazi () {
    this.types.push('Bazi')
    if (!this.enabled) {
      return
    }
    
    // 姓名
    this.userName = typeof this.options.name === 'string' && this.options.name !== '' ? this.options.name : '无名'
    // 标题
    if (typeof this.options.title === 'string' && this.options.title !== '') {
      this.title = this.options.title
    }
    else {
      if (typeof this.options.name === 'string' && this.options.name !== '') {
        this.title = this.options.name + '的生辰八字'
      }
      else {
        this.title = '无标题'
      }
    }
    // 性别,直接获取文字 男 女
    this.sex = this.options.sex
    // 从年支获取生肖
    this.shengxiao = this.date.ganzhi[0].zhi.shengxiao
    // 年龄
    this.age = Bazi.getAge(this.date, CMDate.transDate(new Date()))
    
    // 初始化干支
    this.initGanzhi()
    
    // 排大运
    this.initBigDestiny()
    // 选中大运，并初始化好流年、小运等
    this.selectBigDestiny(this.bigDestiny.curSelected)
    // 如果今年正好在当前这组流年中，则选中流年
    if (this.baziYears.curSelected >= 0) {
      this.selectBaziYears(this.baziYears.curSelected)
    }
    // 流月定位
    if (this.baziMonths.curSelected >= 0) {
      this.selectBaziMonths(this.baziMonths.curSelected)
    }
    // 小运定位
    /*
    if (this.smallDestiny.curSelected >= 0) {
      this.selectSmallDestiny(this.smallDestiny.curSelected)
    }
    */
    // 气运定位
    /*
    if (this.speDestiny.curSelected >= 0) {
      this.selectSpeDestiny(this.speDestiny.curSelected)
    }
    */
  }
  
  // 初始化干支
  initGanzhi () {
    // 只需要对前四柱展开藏干，计算十神即可
    const ganzhiNum = 4
    const dayGan = this.date.ganzhi[2].gan
    for (let i = 0; i < ganzhiNum; i++) {
      let ganzhi = this.date.ganzhi[i]
      // 计算十神
      this.tenGodToDayGan(ganzhi)
      // 展开藏干
      this.setHiddenGan(ganzhi)
    }
    
    // 地利计算
    this.initGroundState()
    // 天时计算
    this.initSkyState()
  }
  
  // 地利计算，获取四柱的天干在地支的十二长生
  initGroundState () {
    // 十二长生列表
    const twelveStateList = God12.godList.twelveState.list
    
    const ganzhiNum = 4
    for (let i = 0; i < ganzhiNum; i++) {
      let src = this.date.ganzhi[i]
      src.gan.relations.twelveStateToZhi = []
      for (let j = 0; j < ganzhiNum; j++) {
        let des = this.date.ganzhi[j]
        const index = src.gan.getTwelveStateIndex(des.zhi.index)
        src.gan.relations.twelveStateToZhi.push(twelveStateList[index])
      }
    }
  }
  
  // 天时计算
  initSkyState () {
    const ganzhiNum = 4
    for (let i = 0; i < ganzhiNum; i++) {
      let ganzhi = this.date.ganzhi[i]
      ganzhi.gan.relations.seasonPower = this.timeGod.seasonPower[ganzhi.gan.wuxing]
    }
  }

  // 排大运
  initBigDestiny () {
    // 判断阳男阴女，阴男阳女
    const reverse = this.isReverse()
    // 寻找前一个或者后一个节
    let desTermTime = null
    if (this.date.nextTerm.index % 2 !== 0 && !reverse) {
      // 顺数，且下一个节气是节，取下一个节气
      desTermTime = this.date.nextTerm.time
    }
    else if (this.date.nextTerm.index % 2 === 0 && reverse) {
      // 逆数，且下一个节气是气，取上一个节气
      desTermTime = this.date.prevTerm.time
    }
    else {
      // 其他特殊情况，下一个节气
      if (!this.date.dayAfterAllTerms) {
        this.date.getYearTerms()
      }
      const nextTermIndex = this.date.dayAfterAllTerms.findIndex((item) => {
        return item.index === this.date.nextTerm.index && item.time.year === this.date.nextTerm.time.year &&
          item.time.month === this.date.nextTerm.time.month && item.time.day === this.date.nextTerm.time.day
      })
      if (reverse) {
        // 下一个节气是节且逆数，取上上一个节气
        desTermTime = this.date.dayAfterAllTerms[nextTermIndex - 2].time
      }
      else {
        // 下一个节气是气且顺数
        if (nextTermIndex + 1 >= this.date.dayAfterAllTerms.length) {
          // 大雪后，冬至前的特殊情况，要计算明年的小寒时间
          const nextMonth = CMDate.getDesMonth(this.date.year, this.date.month, 1)
          const newDate = new CMDate({
            year: nextMonth.year,
            month: nextMonth.month,
            day: this.date.day
          })
          desTermTime = newDate.getYearTermTime(1)
        }
        else {
          // 正常情况，直接取下下个节气
          desTermTime = this.date.dayAfterAllTerms[nextTermIndex + 1].time
        }
      }
    }
    
    // 寻找当前时间距离目标节的时间差
    const rulue = CMDate.dateToRulue(this.date.year, this.date.month, this.date.day, this.date.hour, this.date.min)
    const dayDelta = reverse ? rulue - desTermTime.rulue : desTermTime.rulue - rulue
    // 根据时间差确定出生后，多少年 多少月 多少日 开始起大运
    // 按照一天算1/3年，计算起运天数
    const dayOffset = dayDelta * DESTINY_DAY_VALUE
    // 算出起运时间点
    const bigDestinyStartTime = CMDate.rulueToDate(rulue + dayOffset)
    this.bigDestiny = {
      startTime: bigDestinyStartTime,
      startAge: Bazi.getAge(this.date, bigDestinyStartTime),
      list: [],
      // 当下时间对应哪一步大运，默认为第一步大运
      curSelected: 0,
      selected: 0,
    }
    
    // 排列大运
    // 先获取当下时间，用于定位当前大运位置
    this.nowDate = new CMDate(new Date())
    const curRulue = this.nowDate.rulue
    let prevRulue = null
    let nextRulue = null
    // 是否标记了当前时间点
    let isSet = false
    // 干支推算用
    let curGan = this.date.ganzhi[1].gan.index
    let curZhi = this.date.ganzhi[1].zhi.index
    const ganLen = Gan.ganList.length
    const zhiLen = Zhi.zhiList.length
    const offset = reverse ? -1 : 1
    // 起运前先装进去月干
    this.bigDestiny.list.push({
      ganzhi: this.date.ganzhi[1],
      year: this.date.year,
      age: 0
    })
    // 记录日干对象
    const dayGan = this.date.ganzhi[2].gan
    for (let i = 0; i < BIG_DESTINY_NUM; i++) {
      curGan = (curGan + ganLen + offset) % ganLen
      curZhi = (curZhi + zhiLen + offset) % zhiLen
      const ganzhi = new Ganzhi(curGan, curZhi, null, {
        description: this.description + BaseStage.dictionary.BIG_DESTINY
      })
      
      // 判断大运天干和日干之间的十神关系
      this.tenGodToDayGan(ganzhi)
      
      const obj = {
        ganzhi,
        year: bigDestinyStartTime.year + i * BIG_DESTINY_OFFSET,
        age: this.bigDestiny.startAge + i * BIG_DESTINY_OFFSET
      }
      
      // 当前时间点是否位于这一步大运
      prevRulue = CMDate.dateToRulue(obj.year, bigDestinyStartTime.month, bigDestinyStartTime.day, bigDestinyStartTime.hour, bigDestinyStartTime.min)
      nextRulue = CMDate.dateToRulue(obj.year + BIG_DESTINY_OFFSET, bigDestinyStartTime.month, bigDestinyStartTime.day, bigDestinyStartTime.hour, bigDestinyStartTime.min)
      if (curRulue >= prevRulue && curRulue <= nextRulue) {
        this.bigDestiny.curSelected = i + 1
        isSet = true
      }
      
      this.bigDestiny.list.push(obj)
    }
    
    if (!isSet && curRulue > nextRulue) {
      // 若当前日期超出大运界限，则选中最后一步大运
      this.bigDestiny.curSelected = BIG_DESTINY_NUM - 1
    }
  }

  /* @section set 排盘 */
  
  // 选中一步大运，并且根据当前所处的大运区间，排小运，流年，起运
  // isMonthSet是否排流月
  selectBigDestiny (index, isMonthSet = true) {
    // 选中大运
    this.bigDestiny.selected = index
    // 选中的一步大运解开藏干，起运前就是月干支，无需打开藏干
    if (index > 0) {
      this.setHiddenGan(this.bigDestiny.list[index].ganzhi)
    }
    
    // 排流年
    this.setBaziYears(index)
    // 排小运
    // this.setSmallDestiny(index)
    // 排气运
    // this.setSpeDestiny(index)
    
    // 选中流年、小运、气运
    this.selectBaziYears(0)
    // this.selectSmallDestiny(0)
    // this.selectSpeDestiny(0)
  }
  
  // 选中一步流年，并且根据当前流年，排出流月
  selectBaziYears (index) {
    this.baziYears.selected = index
    // 选中的流年解开藏干
    this.setHiddenGan(this.baziYears.list[index].ganzhi)
    
    // 排流月
    this.setBaziMonths(index)
    // 选流月
    this.selectBaziMonths(0)
  }
  
  // 选小运
  selectSmallDestiny (index) {
    this.smallDestiny.selected = index
    // 选中的小运解开藏干
    this.setHiddenGan(this.smallDestiny.list[index].ganzhi)
  }
  
  // 选气运
  selectSpeDestiny (index) {
    this.speDestiny.selected = index
    // 选中的气运解开藏干
    this.setHiddenGan(this.speDestiny.list[index].ganzhi)
  }
  
  // 选流月
  selectBaziMonths (index) {
    this.baziMonths.selected = index
    // 选中的流月解开藏干
    this.setHiddenGan(this.baziMonths.list[index])
  }
  
  // 排流年
  setBaziYears (index) {
    // 获取当下时间供参考
    if (!this.nowDate) {
      this.nowDate = new CMDate(new Date())
    }
    
    this.baziYears = {
      list: [],
      // 当前的流年是否吻合
      curSelected: -1,
      selected: 0
    }
    const startYear = this.bigDestiny.list[index].age + this.date.ganzhiYear
    let endYear = index + 1 >= this.bigDestiny.list.length ?
      startYear + BIG_DESTINY_OFFSET :
      this.bigDestiny.list[index + 1].age - this.bigDestiny.list[index].age + startYear
    if (endYear === startYear) {
      endYear++
    }
    // 干支起点
    let curGan = this.date.ganzhi[0].gan.nextIndex(this.bigDestiny.list[index].age)
    let curZhi = this.date.ganzhi[0].zhi.nextIndex(this.bigDestiny.list[index].age)
    const ganLen = Gan.ganList.length
    const zhiLen = Zhi.zhiList.length
    for (let i = 0; startYear + i < endYear; i++) {
      const ganzhi = new Ganzhi(curGan, curZhi, null, {
        description: this.description + BaseStage.dictionary.BAZI_YEAR
      })
      
      // 判断流年天干和日干之间的十神关系
      this.tenGodToDayGan(ganzhi)
      
      const obj = {
        ganzhi,
        year: startYear + i,
        age: this.bigDestiny.list[index].age + i
      }
      this.baziYears.list.push(obj)
      
      // 确定当前的流年
      if (this.nowDate.ganzhiYear === obj.year) {
        this.baziYears.curSelected = i
      }
      
      curGan = (curGan + 1) % ganLen
      curZhi = (curZhi + 1) % zhiLen
    }
  }
  
  // 排流月
  setBaziMonths (index) {
    // 获取当下时间供参考
    if (!this.nowDate) {
      this.nowDate = new CMDate(new Date())
    }
    
    let curSelected = -1
    const yearGan = this.baziYears.list[index].ganzhi.gan
    // 日干
    const dayGan = this.date.ganzhi[2].gan
    let result = CMDate.getYearMonths(yearGan, (ganzhi, ganzhiIndex) => {
      // 计算流月和日干的十神关系
      this.tenGodToDayGan(ganzhi)
      
      // 当前月份吻合
      if (index === this.baziYears.curSelected && ganzhi.name === this.nowDate.ganzhi[1].name) {
        curSelected = ganzhiIndex
      }
      return ganzhi
    }, {
      description: this.description + BaseStage.dictionary.BAZI_MONTH
    })
    
    this.baziMonths = {
      list: result,
      // 当前的流年是否吻合
      curSelected: curSelected,
      selected: 0
    }
  }
  
  // 排小运，以后拓展
  setSmallDestiny (index) {
  }
  
  // 排气运，以后拓展
  setSpeDestiny (index) {
  }
  
  // 计算和日干的十神关系，返回处理后的干支
  tenGodToDayGan (ganzhi) {
    const dayGan = this.date.ganzhi[2].gan
    ganzhi.gan.setTenGod(dayGan, 'tenGodToDayGan')
    ganzhi.zhi.setTenGod(dayGan, 'tenGodToDayGan')
    return ganzhi
  }
  
  // 展开藏干，并计算十神
  setHiddenGan (ganzhi) {
    const dayGan = this.date.ganzhi[2].gan
    ganzhi.initHiddenGan()
    const hiddenGansLen = ganzhi.zhi.hiddenGans.length
    for (let i = 0; i < hiddenGansLen; i++) {
      const gan = ganzhi.zhi.hiddenGans[i]
      // 计算藏干十神
      gan.setTenGod(dayGan, 'tenGodToDayGan')
    }
    return ganzhi
  }
  
  /* @section get 获取 */
  // 获取天尊星对象
  showTianzhunStar () {
    return this.wuxingObjs[this.tianzunStar]
  }
  
  // 根据选中的流年获取流月
  getBaziYearMonths (selected) {
    const yearGan = this.baziYears[selected].ganzhi.gan
    // 日干
    const dayGan = this.date.ganzhi[2].gan
    let result = CMDate.getYearMonths(yearGan, (ganzhi) => {
      // 计算流月和日干的十神关系
      ganzhi.gan.setTenGod(dayGan, 'tenGodToDayGan')
      ganzhi.zhi.setTenGod(dayGan, 'tenGodToDayGan')
      return ganzhi
    })
    
    // 天尊八字专有，要把列表进行逆转
    result = result.reverse()
    return result
  }
  
  // 获取顺逆，阳男阴女顺，阴男阳女逆
  isReverse () {
    if ((this.sex === '男' && !this.date.ganzhi[0].gan.yinyang) || (this.sex === '女' && this.date.ganzhi[0].gan.yinyang)) {
      return true
    }
    return false
  }
  
  /* @section static 静态方法 */
  // 获取年龄
  static getAge (src, des) {
    let age = des.year - src.year
    if (des.month < src.month || (des.month === src.month && des.day < src.day)) {
      age--
    }
    return age
  }
}

export default Bazi